home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / chrome_100_percent.pak / Unnamed File 000012.txt < prev    next >
Text File  |  2013-04-03  |  17KB  |  438 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. // Generates the chrome.* API bindings from a list of schemas.
  6.  
  7.   // TODO(battre): cleanup the usage of packages everywhere, as described here
  8.   // http://codereview.chromium.org/10392008/diff/38/chrome/renderer/resources/extensions/schema_generated_bindings.js
  9.  
  10.   require('json_schema');
  11.   require('event_bindings');
  12.   var GetExtensionAPIDefinition =
  13.       requireNative('apiDefinitions').GetExtensionAPIDefinition;
  14.   var sendRequest = require('sendRequest').sendRequest;
  15.   var utils = require('utils');
  16.   var chromeHidden = requireNative('chrome_hidden').GetChromeHidden();
  17.   var schemaUtils = require('schemaUtils');
  18.  
  19.   // The object to generate the bindings for "internal" APIs in, so that
  20.   // extensions can't directly call them (without access to chromeHidden),
  21.   // but are still needed for internal mechanisms of extensions (e.g. events).
  22.   //
  23.   // This is distinct to the "*Private" APIs which are controlled via
  24.   // having strict permissions and aren't generated *anywhere* unless needed.
  25.   var internalAPIs = {};
  26.   chromeHidden.internalAPIs = internalAPIs;
  27.  
  28.   // Stores the name and definition of each API function, with methods to
  29.   // modify their behaviour (such as a custom way to handle requests to the
  30.   // API, a custom callback, etc).
  31.   function APIFunctions() {
  32.     this._apiFunctions = {};
  33.     this._unavailableApiFunctions = {};
  34.   }
  35.   APIFunctions.prototype.register = function(apiName, apiFunction) {
  36.     this._apiFunctions[apiName] = apiFunction;
  37.   };
  38.   // Registers a function as existing but not available, meaning that calls to
  39.   // the set* methods that reference this function should be ignored rather
  40.   // than throwing Errors.
  41.   APIFunctions.prototype.registerUnavailable = function(apiName) {
  42.     this._unavailableApiFunctions[apiName] = apiName;
  43.   };
  44.   APIFunctions.prototype._setHook =
  45.       function(apiName, propertyName, customizedFunction) {
  46.     if (this._unavailableApiFunctions.hasOwnProperty(apiName))
  47.       return;
  48.     if (!this._apiFunctions.hasOwnProperty(apiName))
  49.       throw new Error('Tried to set hook for unknown API "' + apiName + '"');
  50.     this._apiFunctions[apiName][propertyName] = customizedFunction;
  51.   };
  52.   APIFunctions.prototype.setHandleRequest =
  53.       function(apiName, customizedFunction) {
  54.     return this._setHook(apiName, 'handleRequest', customizedFunction);
  55.   };
  56.   APIFunctions.prototype.setUpdateArgumentsPostValidate =
  57.       function(apiName, customizedFunction) {
  58.     return this._setHook(
  59.       apiName, 'updateArgumentsPostValidate', customizedFunction);
  60.   };
  61.   APIFunctions.prototype.setUpdateArgumentsPreValidate =
  62.       function(apiName, customizedFunction) {
  63.     return this._setHook(
  64.       apiName, 'updateArgumentsPreValidate', customizedFunction);
  65.   };
  66.   APIFunctions.prototype.setCustomCallback =
  67.       function(apiName, customizedFunction) {
  68.     return this._setHook(apiName, 'customCallback', customizedFunction);
  69.   };
  70.  
  71.   var apiFunctions = new APIFunctions();
  72.  
  73.   // Wraps the calls to the set* methods of APIFunctions with the namespace of
  74.   // an API, and validates that all calls to set* methods aren't prefixed with
  75.   // a namespace.
  76.   //
  77.   // For example, if constructed with 'browserAction', a call to
  78.   // handleRequest('foo') will be transformed into
  79.   // handleRequest('browserAction.foo').
  80.   //
  81.   // Likewise, if a call to handleRequest is called with 'browserAction.foo',
  82.   // it will throw an error.
  83.   //
  84.   // These help with isolating custom bindings from each other.
  85.   function NamespacedAPIFunctions(namespace, delegate) {
  86.     var self = this;
  87.     function wrap(methodName) {
  88.       self[methodName] = function(apiName, customizedFunction) {
  89.         var prefix = namespace + '.';
  90.         if (apiName.indexOf(prefix) === 0) {
  91.           throw new Error(methodName + ' called with "' + apiName +
  92.                           '" which has a "' + prefix + '" prefix. ' +
  93.                           'This is unnecessary and must be left out.');
  94.         }
  95.         return delegate[methodName].call(delegate,
  96.                                          prefix + apiName, customizedFunction);
  97.       };
  98.     }
  99.  
  100.     wrap('contains');
  101.     wrap('setHandleRequest');
  102.     wrap('setUpdateArgumentsPostValidate');
  103.     wrap('setUpdateArgumentsPreValidate');
  104.     wrap('setCustomCallback');
  105.   }
  106.  
  107.   //
  108.   // The API through which the ${api_name}_custom_bindings.js files customize
  109.   // their API bindings beyond what can be generated.
  110.   //
  111.   // There are 2 types of customizations available: those which are required in
  112.   // order to do the schema generation (registerCustomEvent and
  113.   // registerCustomType), and those which can only run after the bindings have
  114.   // been generated (registerCustomHook).
  115.   //
  116.  
  117.   // Registers a custom event type for the API identified by |namespace|.
  118.   // |event| is the event's constructor.
  119.   var customEvents = {};
  120.   chromeHidden.registerCustomEvent = function(namespace, event) {
  121.     if (typeof(namespace) !== 'string') {
  122.       throw new Error("registerCustomEvent requires the namespace of the " +
  123.                       "API as its first argument");
  124.     }
  125.     customEvents[namespace] = event;
  126.   };
  127.  
  128.   // Registers a function |hook| to run after the schema for all APIs has been
  129.   // generated.  The hook is passed as its first argument an "API" object to
  130.   // interact with, and second the current extension ID. See where
  131.   // |customHooks| is used.
  132.   var customHooks = {};
  133.   chromeHidden.registerCustomHook = function(namespace, fn) {
  134.     if (typeof(namespace) !== 'string') {
  135.       throw new Error("registerCustomHook requires the namespace of the " +
  136.                       "API as its first argument");
  137.     }
  138.     customHooks[namespace] = fn;
  139.   };
  140.  
  141.   function CustomBindingsObject() {
  142.   }
  143.   CustomBindingsObject.prototype.setSchema = function(schema) {
  144.     // The functions in the schema are in list form, so we move them into a
  145.     // dictionary for easier access.
  146.     var self = this;
  147.     self.functionSchemas = {};
  148.     schema.functions.forEach(function(f) {
  149.       self.functionSchemas[f.name] = {
  150.         name: f.name,
  151.         definition: f
  152.       }
  153.     });
  154.   };
  155.  
  156.   // Registers a custom type referenced via "$ref" fields in the API schema
  157.   // JSON.
  158.   var customTypes = {};
  159.   chromeHidden.registerCustomType = function(typeName, customTypeFactory) {
  160.     var customType = customTypeFactory();
  161.     customType.prototype = new CustomBindingsObject();
  162.     customTypes[typeName] = customType;
  163.   };
  164.  
  165.   // Get the platform from navigator.appVersion.
  166.   function getPlatform() {
  167.     var platforms = [
  168.       [/CrOS Touch/, "chromeos touch"],
  169.       [/CrOS/, "chromeos"],
  170.       [/Linux/, "linux"],
  171.       [/Mac/, "mac"],
  172.       [/Win/, "win"],
  173.     ];
  174.  
  175.     for (var i = 0; i < platforms.length; i++) {
  176.       if (platforms[i][0].test(navigator.appVersion)) {
  177.         return platforms[i][1];
  178.       }
  179.     }
  180.     return "unknown";
  181.   }
  182.  
  183.   function isPlatformSupported(schemaNode, platform) {
  184.     return !schemaNode.platforms ||
  185.         schemaNode.platforms.indexOf(platform) > -1;
  186.   }
  187.  
  188.   function isManifestVersionSupported(schemaNode, manifestVersion) {
  189.     return !schemaNode.maximumManifestVersion ||
  190.         manifestVersion <= schemaNode.maximumManifestVersion;
  191.   }
  192.  
  193.   function isSchemaNodeSupported(schemaNode, platform, manifestVersion) {
  194.     return isPlatformSupported(schemaNode, platform) &&
  195.         isManifestVersionSupported(schemaNode, manifestVersion);
  196.   }
  197.  
  198.   chromeHidden.onLoad.addListener(function(extensionId,
  199.                                            contextType,
  200.                                            isIncognitoProcess,
  201.                                            manifestVersion) {
  202.     var apiDefinitions = GetExtensionAPIDefinition();
  203.  
  204.     // Read api definitions and setup api functions in the chrome namespace.
  205.     var platform = getPlatform();
  206.  
  207.     apiDefinitions.forEach(function(apiDef) {
  208.       // TODO(kalman): Remove this, or refactor schema_generated_bindings.js so
  209.       // that it isn't necessary. For now, chrome.app and chrome.webstore are
  210.       // entirely handwritten.
  211.       if (['app', 'webstore'].indexOf(apiDef.namespace) >= 0)
  212.         return;
  213.  
  214.       if (!isSchemaNodeSupported(apiDef, platform, manifestVersion))
  215.         return;
  216.  
  217.       // See comment on internalAPIs at the top.
  218.       var mod = apiDef.internal ? internalAPIs : chrome;
  219.  
  220.       var namespaces = apiDef.namespace.split('.');
  221.       for (var index = 0, name; name = namespaces[index]; index++) {
  222.         mod[name] = mod[name] || {};
  223.         mod = mod[name];
  224.       }
  225.  
  226.       // Add types to global schemaValidator
  227.       if (apiDef.types) {
  228.         apiDef.types.forEach(function(t) {
  229.           if (!isSchemaNodeSupported(t, platform, manifestVersion))
  230.             return;
  231.  
  232.           schemaUtils.schemaValidator.addTypes(t);
  233.           if (t.type == 'object' && customTypes[t.id]) {
  234.             customTypes[t.id].prototype.setSchema(t);
  235.           }
  236.         });
  237.       }
  238.  
  239.       // Returns whether access to the content of a schema should be denied,
  240.       // based on the presence of "unprivileged" and whether this is an
  241.       // extension process (versus e.g. a content script).
  242.       function isSchemaAccessAllowed(itemSchema) {
  243.         return (contextType == 'BLESSED_EXTENSION') ||
  244.                apiDef.unprivileged ||
  245.                itemSchema.unprivileged;
  246.       }
  247.  
  248.       // Adds a getter that throws an access denied error to object |mod|
  249.       // for property |name|.
  250.       function addUnprivilegedAccessGetter(mod, name) {
  251.         mod.__defineGetter__(name, function() {
  252.           throw new Error(
  253.               '"' + name + '" can only be used in extension processes. See ' +
  254.               'the content scripts documentation for more details.');
  255.         });
  256.       }
  257.  
  258.       // Setup Functions.
  259.       if (apiDef.functions) {
  260.         apiDef.functions.forEach(function(functionDef) {
  261.           if (functionDef.name in mod) {
  262.             throw new Error('Function ' + functionDef.name +
  263.                             ' already defined in ' + apiDef.namespace);
  264.           }
  265.  
  266.           var apiFunctionName = apiDef.namespace + "." + functionDef.name;
  267.  
  268.           if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) {
  269.             apiFunctions.registerUnavailable(apiFunctionName);
  270.             return;
  271.           }
  272.           if (!isSchemaAccessAllowed(functionDef)) {
  273.             apiFunctions.registerUnavailable(apiFunctionName);
  274.             addUnprivilegedAccessGetter(mod, functionDef.name);
  275.             return;
  276.           }
  277.  
  278.           var apiFunction = {};
  279.           apiFunction.definition = functionDef;
  280.           apiFunction.name = apiFunctionName;
  281.  
  282.           // TODO(aa): It would be best to run this in a unit test, but in order
  283.           // to do that we would need to better factor this code so that it
  284.           // doesn't depend on so much v8::Extension machinery.
  285.           if (chromeHidden.validateAPI &&
  286.               schemaUtils.isFunctionSignatureAmbiguous(
  287.                   apiFunction.definition)) {
  288.             throw new Error(
  289.                 apiFunction.name + ' has ambiguous optional arguments. ' +
  290.                 'To implement custom disambiguation logic, add ' +
  291.                 '"allowAmbiguousOptionalArguments" to the function\'s schema.');
  292.           }
  293.  
  294.           apiFunctions.register(apiFunction.name, apiFunction);
  295.  
  296.           mod[functionDef.name] = (function() {
  297.             var args = Array.prototype.slice.call(arguments);
  298.             if (this.updateArgumentsPreValidate)
  299.               args = this.updateArgumentsPreValidate.apply(this, args);
  300.  
  301.             args = schemaUtils.normalizeArgumentsAndValidate(args, this);
  302.             if (this.updateArgumentsPostValidate)
  303.               args = this.updateArgumentsPostValidate.apply(this, args);
  304.  
  305.             var retval;
  306.             if (this.handleRequest) {
  307.               retval = this.handleRequest.apply(this, args);
  308.             } else {
  309.               var optArgs = {
  310.                 customCallback: this.customCallback
  311.               };
  312.               retval = sendRequest(this.name, args,
  313.                                    this.definition.parameters,
  314.                                    optArgs);
  315.             }
  316.  
  317.             // Validate return value if defined - only in debug.
  318.             if (chromeHidden.validateCallbacks &&
  319.                 this.definition.returns) {
  320.               schemaUtils.validate([retval], [this.definition.returns]);
  321.             }
  322.             return retval;
  323.           }).bind(apiFunction);
  324.         });
  325.       }
  326.  
  327.       // Setup Events
  328.       if (apiDef.events) {
  329.         apiDef.events.forEach(function(eventDef) {
  330.           if (eventDef.name in mod) {
  331.             throw new Error('Event ' + eventDef.name +
  332.                             ' already defined in ' + apiDef.namespace);
  333.           }
  334.           if (!isSchemaNodeSupported(eventDef, platform, manifestVersion))
  335.             return;
  336.           if (!isSchemaAccessAllowed(eventDef)) {
  337.             addUnprivilegedAccessGetter(mod, eventDef.name);
  338.             return;
  339.           }
  340.  
  341.           var eventName = apiDef.namespace + "." + eventDef.name;
  342.           var customEvent = customEvents[apiDef.namespace];
  343.           var options = eventDef.options || {};
  344.  
  345.           if (eventDef.filters && eventDef.filters.length > 0)
  346.             options.supportsFilters = true;
  347.  
  348.           if (customEvent) {
  349.             mod[eventDef.name] = new customEvent(
  350.                 eventName, eventDef.parameters, eventDef.extraParameters,
  351.                 options);
  352.           } else if (eventDef.anonymous) {
  353.             mod[eventDef.name] = new chrome.Event();
  354.           } else {
  355.             mod[eventDef.name] = new chrome.Event(
  356.                 eventName, eventDef.parameters, options);
  357.           }
  358.         });
  359.       }
  360.  
  361.       function addProperties(m, parentDef) {
  362.         var properties = parentDef.properties;
  363.         if (!properties)
  364.           return;
  365.  
  366.         utils.forEach(properties, function(propertyName, propertyDef) {
  367.           if (propertyName in m)
  368.             return;  // TODO(kalman): be strict like functions/events somehow.
  369.           if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion))
  370.             return;
  371.           if (!isSchemaAccessAllowed(propertyDef)) {
  372.             addUnprivilegedAccessGetter(m, propertyName);
  373.             return;
  374.           }
  375.  
  376.           var value = propertyDef.value;
  377.           if (value) {
  378.             // Values may just have raw types as defined in the JSON, such
  379.             // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here.
  380.             // TODO(kalman): enforce that things with a "value" property can't
  381.             // define their own types.
  382.             var type = propertyDef.type || typeof(value);
  383.             if (type === 'integer' || type === 'number') {
  384.               value = parseInt(value);
  385.             } else if (type === 'boolean') {
  386.               value = value === "true";
  387.             } else if (propertyDef["$ref"]) {
  388.               var constructor = customTypes[propertyDef["$ref"]];
  389.               if (!constructor)
  390.                 throw new Error("No custom binding for " + propertyDef["$ref"]);
  391.               var args = value;
  392.               // For an object propertyDef, |value| is an array of constructor
  393.               // arguments, but we want to pass the arguments directly (i.e.
  394.               // not as an array), so we have to fake calling |new| on the
  395.               // constructor.
  396.               value = { __proto__: constructor.prototype };
  397.               constructor.apply(value, args);
  398.               // Recursively add properties.
  399.               addProperties(value, propertyDef);
  400.             } else if (type === 'object') {
  401.               // Recursively add properties.
  402.               addProperties(value, propertyDef);
  403.             } else if (type !== 'string') {
  404.               throw new Error("NOT IMPLEMENTED (extension_api.json error): " +
  405.                   "Cannot parse values for type \"" + type + "\"");
  406.             }
  407.             m[propertyName] = value;
  408.           }
  409.         });
  410.       }
  411.  
  412.       addProperties(mod, apiDef);
  413.     });
  414.  
  415.     // Run the non-declarative custom hooks after all the schemas have been
  416.     // generated, in case hooks depend on other APIs being available.
  417.     apiDefinitions.forEach(function(apiDef) {
  418.       if (!isSchemaNodeSupported(apiDef, platform, manifestVersion))
  419.         return;
  420.  
  421.       var hook = customHooks[apiDef.namespace];
  422.       if (!hook)
  423.         return;
  424.  
  425.       // Pass through the public API of schema_generated_bindings, to be used
  426.       // by custom bindings JS files. Create a new one so that bindings can't
  427.       // interfere with each other.
  428.       hook({
  429.         apiFunctions: new NamespacedAPIFunctions(apiDef.namespace,
  430.                                                  apiFunctions),
  431.         apiDefinitions: apiDefinitions,
  432.       }, extensionId, contextType);
  433.     });
  434.  
  435.     if (chrome.test)
  436.       chrome.test.getApiDefinitions = GetExtensionAPIDefinition;
  437.   });
  438.